#include "tracker.h"

/*******************************************************************************
* Constructors
*******************************************************************************/
Tracker::Tracker(Torrent *torrent, string hostname, unsigned port, string announce_dir)
{
	m_socket = -1;
	m_hostname = hostname;
	m_port = port;
	m_announce_dir = announce_dir;
	m_torrent = torrent;
}
Tracker::~Tracker()
{
//	cout<<"tracker deleted"<<endl;
	close(m_socket);
}

/*******************************************************************************
* BitTorrent Actions
*******************************************************************************/
bool Tracker::Connect()
{
	m_socket = ConnectToHost(m_hostname.c_str(), GetTrackerPort().c_str());
	if (m_socket == -1)
	{
		return false;
	}
	return true;
}
bool Tracker::Disconnect()
{
	close(m_socket);
	m_socket = -1;
	return true;
}
bool Tracker::RequestPeers(unsigned num_peers)
{
	// string to hold HTTP GET request
	string request("GET ");
	request += m_announce_dir;

	// info hash
	char buf[61]; int j = 0;
	for (int i = 0; i < 60; i++)
	{
		if (i % 3 != 0)
		{
			buf[i] = m_torrent->GetInfoHash().c_str()[j];
			j++;
		}
		else
		{
			buf[i] = '%';
		}
	}
	buf[60] = '\0';
	string info_hash(buf);

	// client id
	j = 0;
	char client_id[] = "1234567890123456789012345678901234567890";
	for (int i = 0; i < 60; i++)
	{
		if (i % 3 != 0)
		{
			buf[i] = client_id[j];
			j++;
		}
		else
		{
			buf[i] = '%';
		}
	}
	buf[60] = '\0';
	string peer_id(buf);

	// other fields
	string compact("1");
	string event("started");
	sprintf(buf, "%i", 6881);
	string port_num(buf);
	sprintf(buf, "%i", m_torrent->GetFileSize());
	string left(buf);
	sprintf(buf, "%i", m_torrent->GetUploaded());
	string uploaded(buf);
	sprintf(buf, "%i", m_torrent->GetDownloaded());
	string downloaded(buf);
	sprintf(buf, "%i", num_peers);
	string numwant(buf);

	// build request
	request += "?info_hash="	+ info_hash;
	request += "&peer_id="		+ peer_id;
	request += "&port="			+ port_num;
	request += "&uploaded="		+ uploaded;
	request += "&downloaded="	+ downloaded;
	request += "&left="			+ left;
	request += "&compact="		+ compact;
	request += "&event="		+ event;
	request += "&numwant="		+ numwant;
	request += " HTTP/1.1\r\n\r\n";

	// send to tracker
	write(m_socket, request.c_str(), request.length());

	// wait for response
	char response[1<<23];
	int num = read(m_socket, response, sizeof(response));
	response[num] = 0;
	if (memcmp("HTTP/1.1 200 OK", response, 15) != 0 &&
		memcmp("HTTP/1.0 200 OK", response, 15) != 0)
	{
		printf("Invalid response from tracker.\n");
		return false;
	}
	int i;
	for (i = 4; memcmp(&(response[i-4]), "\r\n\r\n", 4) != 0; i++);

	// decode response from tracker
	bDictionary *dictionary = (bDictionary*)bDecode(&(response[i]));
	bString* peer_string = (bString*)dictionary->GetItemFromKey(string("peers"));
	unsigned peers_returned = peer_string->GetStringLength()/6;
	unsigned char peer_info[peers_returned*6];
	peer_string->GetByteString((char*)peer_info);
	for (unsigned i = 0; i < sizeof(peer_info); i+=6)
	{
		unsigned char ip[4] = { peer_info[i+0], peer_info[i+1], peer_info[i+2], peer_info[i+3] };
		unsigned short port = (peer_info[i+4]<<8) + peer_info[i+5];
		Peer *tmpPeer = new Peer(m_torrent, ip, port);

		m_torrent->AddPeer(tmpPeer);
	}

	// clean up and return
	delete dictionary;
	return true;
}

/*******************************************************************************
* Tracker Data
*******************************************************************************/
string Tracker::GetTrackerHostName()
{
	return m_hostname;
}
string Tracker::GetTrackerPort()
{
	char buf[8];
	sprintf(buf, "%i", m_port);
	return string(buf);
}
string Tracker::GetAnnounceDir()
{
	return m_announce_dir;
}
